package stone926.mods.more_enchantments.enchantments;

import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentTarget;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.projectile.ArrowEntity;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.entity.projectile.TridentEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import stone926.mods.more_enchantments.MoreEnchantmentsMod;
import stone926.mods.more_enchantments.enums.ArrowSpawnPosition;
import stone926.mods.more_enchantments.interfaces.IPersistentProjectileEntity;
import stone926.mods.more_enchantments.interfaces.ITrident;
import stone926.mods.more_enchantments.mixins.accessors.ArrowEntityAccessor;
import stone926.mods.more_enchantments.mixins.accessors.TridentAccessor;

import java.util.Random;

import static stone926.mods.more_enchantments.util.EnchantmentUtil.ALL_ARMOR;

public class TenThousandArrowsEnchantment extends Enchantment {

  private ArrowSpawnPosition position = ArrowSpawnPosition.SHOOTER;

  public TenThousandArrowsEnchantment(ArrowSpawnPosition position) {
    super(Rarity.RARE, EnchantmentTarget.ARMOR, ALL_ARMOR);
  }

  public static double getProjectileYVelocity(int lvl, Random random) {
    double d = 4 + random.nextDouble();
    return -lvl / d;
  }

  public static int getSpawnRadius(int lvl) {
    return lvl + 1;
  }

  public static void spawnArrowsNearEntity(ServerWorld server, LivingEntity shooter, Entity target, PersistentProjectileEntity baseProjectile, int lvl, ArrowSpawnPosition position) {
    if (lvl <= 0) return;
    BlockPos entityPos;
    int i = 5 + shooter.getRandom().nextInt(10);
    if (position == ArrowSpawnPosition.SHOOTER) entityPos = shooter.getBlockPos().withY((int) Math.round(shooter.getY() + shooter.getHeight()) + i);
    else entityPos = target.getBlockPos().withY((int) Math.round(target.getY() + target.getHeight()) + i);
    int r = getSpawnRadius(lvl);
    Iterable<BlockPos> iterator = BlockPos.iterate(entityPos.add(r, 0, r), entityPos.add(-r, 0, -r));
    for (BlockPos pos : iterator) {
      server.spawnParticles(ParticleTypes.END_ROD, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 1, 0.1, 0.5, 0.1, 0);
      PersistentProjectileEntity projectile = (PersistentProjectileEntity) baseProjectile.getType().create(server);
      if (projectile != null) {
        if (projectile.getType() == EntityType.ARROW) {
          for (StatusEffectInstance effect : ((ArrowEntityAccessor) baseProjectile).getPotion().getEffects()) {
            ((ArrowEntity) projectile).addEffect(new StatusEffectInstance(
              effect.getEffectType(),
              Math.max(effect.getDuration() / 3, 1),
              effect.getAmplifier(),
              effect.isAmbient(),
              effect.shouldShowParticles()
            ));
          }
        }
        projectile.setOnFireFor(baseProjectile.getFireTicks());
        projectile.setPos(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5);
        projectile.setVelocity(0, getProjectileYVelocity(lvl, shooter.getRandom()), 0);
        if (lvl > 5) projectile.setOwner(target);
        else projectile.setOwner(shooter);
        projectile.setDamage(getArrowDamage(lvl, shooter.getRandom()) + baseProjectile.getDamage() / 2F);
        projectile.pickupType = PersistentProjectileEntity.PickupPermission.CREATIVE_ONLY;
        IPersistentProjectileEntity projectile1 = (IPersistentProjectileEntity) projectile;
        projectile1.setEntityNotDamage(shooter.getUuid());
        projectile1.setFromEnch(true);
        projectile1.setTraction(((IPersistentProjectileEntity) baseProjectile).getTraction());
        projectile1.setDisappearCountdown(10);
        projectile1.setForceDamage(true);
        server.spawnEntity(projectile);
      }
    }
  }

  public static void spawnTridentNearEntity(ServerWorld server, LivingEntity shooter, Entity target, TridentEntity baseTrident, int lvl, ArrowSpawnPosition position) {
    if (lvl <= 0) return;
    BlockPos entityPos;
    int i = 10 + shooter.getRandom().nextInt(5);
    if (position == ArrowSpawnPosition.SHOOTER) entityPos = shooter.getBlockPos().withY((int) Math.round(shooter.getY() + shooter.getHeight()) + i);
    else entityPos = target.getBlockPos().withY((int) Math.round(target.getY() + target.getHeight()) + i);
    int r = (int) (getSpawnRadius(lvl) * 1.3);
    Iterable<BlockPos> iterator = BlockPos.iterate(entityPos.add(r, 0, r), entityPos.add(-r, 0, -r));
    for (BlockPos pos : iterator) {
      server.spawnParticles(MoreEnchantmentsMod.TRIDENT_RAIN, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 1, 0.1, 0.5, 0.1, 0);
      TridentEntity trident = (TridentEntity) baseTrident.getType().create(server);
      if (trident != null) {
        trident.setOnFireFor(baseTrident.getFireTicks());
        trident.setPos(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5);
        trident.setVelocity(0, getProjectileYVelocity(lvl, shooter.getRandom()) / 2F, 0);
        if (lvl > 5) trident.setOwner(target);
        else trident.setOwner(shooter);
        trident.pickupType = PersistentProjectileEntity.PickupPermission.CREATIVE_ONLY;
        ((TridentAccessor) trident).setTridentStack(((TridentAccessor) baseTrident).invokeAsItemStack());
        ((ITrident) trident).setExtraDamage(getTridentDamage(lvl, shooter.getRandom()));
        IPersistentProjectileEntity trident1 = (IPersistentProjectileEntity) trident;
        trident1.setEntityNotDamage(shooter.getUuid());
        trident1.setFromEnch(true);
        trident1.setTraction(((IPersistentProjectileEntity) baseTrident).getTraction());
        trident1.setDisappearCountdown(1);
        trident1.setForceDamage(true);
        server.spawnEntity(trident);
      }
    }
  }

  private static float getTridentDamage(int lvl, Random random) {
    return lvl * 1.3F + random.nextInt(lvl * 2);
  }

  private static float getArrowDamage(int lvl, Random random) {
    return lvl * 1.3F + random.nextFloat() * 2.2F;
  }

  public ArrowSpawnPosition getPosition() {
    return position;
  }

  public TenThousandArrowsEnchantment setPosition(ArrowSpawnPosition position) {
    this.position = position;
    return this;
  }

  public int getMinPower(int level) {
    return 30;
  }

  public int getMaxPower(int level) {
    return getMinPower(level) + 10 * level;
  }

  public int getMaxLevel() {
    return 6;
  }

  @Override
  protected boolean canAccept(Enchantment other) {
    return super.canAccept(other) && other != MoreEnchantmentsMod.THE_TARGET_OF_ALL;
  }

  @Override
  public boolean isAcceptableItem(ItemStack stack) {
    return super.isAcceptableItem(stack) || stack.isOf(Items.ELYTRA);
  }

}
